#ifndef PUDGE_META_TUPLE_HPP
#define PUDGE_META_TUPLE_HPP

#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>

namespace pudge { namespace meta {

	// code precompiles like this
	//template
	//	<	class T0 = null_tuple_element
	//	,	class T1 = null_tuple_element
	//	,	class T2 = null_tuple_element >
	//struct tuple
	//{
	//	typedef T0 t0_type;
	//	typedef T1 t1_type;
	//	typedef T2 t2_type;
	//	T0 t0;
	//	T1 t1;
	//	T2 t2;
	//	tuple(T0 t0_, T1 t1_, T2 t2_)
	//		:	t0(t0_)
	//		,	t1(t1_)
	//		,	t2(t2_) {}
	//};

#ifndef PUDGE_META_TUPLE_PARAMS
#	define PUDGE_META_TUPLE_PARAMS 7
#endif

	struct null_tuple_element { };

#define PUDGE_META_TUPLE_M(Z,N,A) class T ## N = null_tuple_element
	template
		<	BOOST_PP_ENUM(PUDGE_META_TUPLE_PARAMS, PUDGE_META_TUPLE_M,) >
	struct tuple
	{
#undef PUDGE_META_TUPLE_M
#define PUDGE_META_TUPLE_M(Z,N,A) typedef T ## N t ## N ## _type;
		BOOST_PP_REPEAT(PUDGE_META_TUPLE_PARAMS, PUDGE_META_TUPLE_M,);
#undef PUDGE_META_TUPLE_M

#define PUDGE_META_TUPLE_M(Z,N,A) T ## N t ## N;
		BOOST_PP_REPEAT(PUDGE_META_TUPLE_PARAMS, PUDGE_META_TUPLE_M,);
#undef PUDGE_META_TUPLE_M

#define PUDGE_META_TUPLE_M(Z,N,A) T ## N t ## N ## _
		tuple(BOOST_PP_ENUM(PUDGE_META_TUPLE_PARAMS,PUDGE_META_TUPLE_M,))
#undef PUDGE_META_TUPLE_M

#define PUDGE_META_TUPLE_M(Z,N,A) t ## N(t ## N)
			:	BOOST_PP_ENUM(PUDGE_META_TUPLE_PARAMS,PUDGE_META_TUPLE_M,) {}
#undef PUDGE_META_TUPLE_M
	};

	template<> struct tuple<void> 
	{
#undef PUDGE_META_TUPLE_M
#define PUDGE_META_TUPLE_M(Z,N,A) typedef int t ## N ## _type;
		BOOST_PP_REPEAT(PUDGE_META_TUPLE_PARAMS, PUDGE_META_TUPLE_M,);
#undef PUDGE_META_TUPLE_M
	};

	template<int i> struct tuple_element {};

	template<class T> struct deref { typedef T type; };
	template<class T> struct deref<T&> { typedef T type; };

	template<> struct tuple_element<0>
	{
		template<class T> struct result { typedef typename T :: t0_type type; };
		template<class T> 
		typename deref<typename T :: t0_type> :: type & operator()(T & t) { return t.t0; }
	};

#define BOOST_PP_FILENAME_1 "pudge/meta/detail/unroll_tuple.hpp"
#define BOOST_PP_ITERATION_LIMITS (1,BOOST_PP_DEC(PUDGE_META_TUPLE_PARAMS))
#include BOOST_PP_ITERATE()

}}

#endif